// Code copied from [Rome](https://github.com/rome/tools/blob/lsp/v0.28.0/npm/rome/scripts/generate-packages.mjs)

// oxlint-disable no-console

import * as fs from "node:fs";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";

const OXFMT_BIN_NAME = "oxfmt";
const OXFMT_ROOT = resolve(fileURLToPath(import.meta.url), "../.."); // <REPO ROOT>/npm/oxfmt
const PACKAGES_ROOT = resolve(OXFMT_ROOT, ".."); // <REPO ROOT>/npm
const REPO_ROOT = resolve(PACKAGES_ROOT, "..");
const MANIFEST_PATH = resolve(OXFMT_ROOT, "package.json"); // <REPO ROOT>/npm/oxfmt/package.json
const OXFMT_DIST_SRC = resolve(REPO_ROOT, "apps/oxfmt/dist"); // <REPO ROOT>/apps/oxfmt/dist
const OXFMT_DIST_DEST = resolve(OXFMT_ROOT, "dist"); // <REPO ROOT>/npm/oxfmt/dist

const rootManifest = JSON.parse(fs.readFileSync(MANIFEST_PATH).toString("utf-8"));

const LIBC_MAPPING = {
  gnu: "glibc",
  musl: "musl",
};

function generateNativePackage(target) {
  const packageName = `@${OXFMT_BIN_NAME}/${target}`;
  const packageRoot = resolve(PACKAGES_ROOT, `${OXFMT_BIN_NAME}-${target}`);

  // Remove the directory just in case it already exists (it's autogenerated
  // so there shouldn't be anything important there anyway)
  fs.rmSync(packageRoot, { recursive: true, force: true });

  // Create the package directory
  console.log(`Create directory ${packageRoot}`);
  fs.mkdirSync(packageRoot);

  // Generate the package.json manifest
  const { version, author, license, homepage, bugs, repository } = rootManifest;

  const triple = target.split("-");
  const platform = triple[0];
  const arch = triple[1];
  const libc = triple[2] && { libc: [LIBC_MAPPING[triple[2]]] };
  const manifest = {
    name: packageName,
    version,
    type: "commonjs",
    main: `${OXFMT_BIN_NAME}.${target}.node`,
    author,
    license,
    homepage,
    bugs,
    repository,
    os: [platform],
    cpu: [arch],
    ...libc,
  };

  const manifestPath = resolve(packageRoot, "package.json");
  console.log(`Create manifest ${manifestPath}`);
  fs.writeFileSync(manifestPath, JSON.stringify(manifest));

  // Copy the binary
  const oxfmtBinSource = resolve(REPO_ROOT, `${OXFMT_BIN_NAME}.${target}.node`);
  const oxfmtBinTarget = resolve(packageRoot, `${OXFMT_BIN_NAME}.${target}.node`);

  console.log(`Copy formatter binary ${oxfmtBinSource}`);
  fs.copyFileSync(oxfmtBinSource, oxfmtBinTarget);
}

function writeManifest() {
  const manifestPath = resolve(PACKAGES_ROOT, OXFMT_BIN_NAME, "package.json");

  const manifestData = JSON.parse(fs.readFileSync(manifestPath).toString("utf-8"));

  const nativePackages = TARGETS.map((target) => [
    `@${OXFMT_BIN_NAME}/${target}`,
    rootManifest.version,
  ]);

  manifestData.version = rootManifest.version;
  manifestData.optionalDependencies = Object.fromEntries(nativePackages);

  console.log(`Update manifest ${manifestPath}`);
  const content = JSON.stringify(manifestData);
  fs.writeFileSync(manifestPath, content);
}

// Copy `dist` directory from `apps/oxfmt/dist` to `npm/oxfmt/dist`.
// `apps/oxfmt/scripts/build.js` must be run before this script to create the `dist` directory.
function copyDistFiles() {
  fs.cpSync(OXFMT_DIST_SRC, OXFMT_DIST_DEST, { recursive: true });
}

// NOTE: Must update npm/oxfmt/bin/oxfmt
// and npm/oxfmt/bin/oxc_language_server
const TARGETS = [
  "win32-x64",
  "win32-arm64",
  "linux-x64-gnu",
  "linux-arm64-gnu",
  "linux-x64-musl",
  "linux-arm64-musl",
  "darwin-x64",
  "darwin-arm64",
];

for (const target of TARGETS) {
  generateNativePackage(target);
}

writeManifest();
copyDistFiles();
